/**
 * @file control_data.h
 * @brief 遥控数据模块 - 处理遥控命令相关的所有数据类型
 * @details 本头文件定义了104协议中遥控命令数据类型的结构体和操作函数，
 *          包括单点命令、双点命令、调节命令、设定命令等。
 *          支持完整的数据创建、转换和验证功能。
 * 
 * @author zyb
 * @date 2025-10-11
 * @version 1.0
 * 
 * @note 所有数据类型都遵循IEC 60870-5-104协议规范
 * @warning 遥控命令涉及设备操作，需要严格验证
 * 
 * @see protocol_types.h
 * @see error_codes.h
 * @see validation_utils.h
 */

#ifndef CONTROL_DATA_H
#define CONTROL_DATA_H

#include "../../common/types/include/protocol_types.h"

#ifdef __cplusplus
extern "C" {
#endif

/* ==================== 遥控数据类型定义 ==================== */

/**
 * @brief 单点命令品质描述词 (Single Command Quality)
 */
typedef struct {
    uint8_t scs : 1;    /* 单点命令状态 */
    uint8_t res : 3;    /* 预留 */
    uint8_t bl : 1;     /* 闭锁 */
    uint8_t sb : 1;     /* 取代 */
    uint8_t nt : 1;     /* 非当前 */
    uint8_t iv : 1;     /* 无效 */
} scq_t;

/**
 * @brief 双点命令品质描述词 (Double Command Quality)
 */
typedef struct {
    uint8_t dcs : 2;    /* 双点命令状态 */
    uint8_t res : 2;    /* 预留 */
    uint8_t bl : 1;     /* 闭锁 */
    uint8_t sb : 1;     /* 取代 */
    uint8_t nt : 1;     /* 非当前 */
    uint8_t iv : 1;     /* 无效 */
} dcq_t;

/**
 * @brief 单点命令结构 (C_SC_NA_1)
 */
typedef struct {
    info_addr_t info_address;   /* 信息对象地址 */
    scq_t quality;              /* 品质描述词 */
} C_SC_NA_1_t;

/**
 * @brief 双点命令结构 (C_DC_NA_1)
 */
typedef struct {
    info_addr_t info_address;   /* 信息对象地址 */
    dcq_t quality;              /* 品质描述词 */
} C_DC_NA_1_t;

/**
 * @brief 带CP56Time2a时标的单点命令结构 (C_SC_TA_1)
 */
typedef struct {
    info_addr_t info_address;   /* 信息对象地址 */
    scq_t quality;              /* 品质描述词 */
    cp56time2a_t timestamp;     /* CP56Time2a时间戳 */
} C_SC_TA_1_t;

/**
 * @brief 带CP56Time2a时标的双点命令结构 (C_DC_TA_1)
 */
typedef struct {
    info_addr_t info_address;   /* 信息对象地址 */
    dcq_t quality;              /* 品质描述词 */
    cp56time2a_t timestamp;     /* CP56Time2a时间戳 */
} C_DC_TA_1_t;

/**
 * @brief 调节命令结构 (C_RC_NA_1)
 */
typedef struct {
    info_addr_t info_address;   /* 信息对象地址 */
    s16 value;                  /* 调节值 */
    qoc_t quality;              /* 命令限定词 */
} C_RC_NA_1_t;

/**
 * @brief 遥控数据点配置
 */
typedef struct {
    u16 point_id;           /* 点号 */
    char name[64];          /* 名称 */
    u8 command_type;        /* 命令类型：0-单点，1-双点 */
    u8 default_state;       /* 默认状态 */
} remote_control_point_config_t;

/**
 * @brief 遥控命令数据
 */
typedef struct {
    u16 point_id;           /* 点号 */
    u8 command_value;       /* 命令值 */
    u8 command_type;        /* 命令类型：0-单点，1-双点 */
    cp56time2a_t timestamp; /* 时间戳 */
    u8 execute_result;      /* 执行结果：0-成功，1-失败 */
} remote_control_command_t;

/**
 * @brief 遥控数据组
 */
typedef struct {
    remote_control_command_t* commands; /* 遥控命令数组 */
    u32 count;                           /* 命令个数 */
    u32 max_count;                       /* 最大命令个数 */
} remote_control_data_group_t;

/* ==================== 遥控数据操作函数 ==================== */

/**
 * @brief 创建遥控数据点配置
 * @param config 遥控数据点配置结构指针
 * @param point_id 点号
 * @param name 名称
 * @param command_type 命令类型
 * @param default_state 默认状态
 * @return 协议错误码
 */
protocol_error_t create_remote_control_point_config(remote_control_point_config_t* config,
                                                   u16 point_id, const char* name,
                                                   u8 command_type, u8 default_state);

/**
 * @brief 创建遥控命令数据
 * @param command 遥控命令数据结构指针
 * @param point_id 点号
 * @param command_value 命令值
 * @param command_type 命令类型
 * @param timestamp 时间戳
 * @return 协议错误码
 */
protocol_error_t create_remote_control_command(remote_control_command_t* command,
                                              u16 point_id, u8 command_value,
                                              u8 command_type,
                                              const cp56time2a_t* timestamp);

/**
 * @brief 创建遥控数据组
 * @param group 遥控数据组结构指针
 * @param max_count 最大命令个数
 * @return 协议错误码
 */
protocol_error_t create_remote_control_data_group(remote_control_data_group_t* group, u32 max_count);

/**
 * @brief 添加遥控命令到组
 * @param group 遥控数据组结构指针
 * @param command 遥控命令数据
 * @return 协议错误码
 */
protocol_error_t add_remote_control_command_to_group(remote_control_data_group_t* group,
                                                    const remote_control_command_t* command);

/**
 * @brief 验证遥控命令值
 * @param command_value 命令值
 * @param command_type 命令类型
 * @return 协议错误码
 */
protocol_error_t validate_remote_control_command_value(u8 command_value, u8 command_type);

/**
 * @brief 根据点号查找遥控数据点配置
 * @param configs 遥控数据点配置数组
 * @param count 配置个数
 * @param point_id 点号
 * @return 遥控数据点配置指针，未找到返回NULL
 */
const remote_control_point_config_t* find_remote_control_point_config(const remote_control_point_config_t* configs,
                                                                     u32 count, u16 point_id);

/* ==================== 遥控数据转换函数 ==================== */

/**
 * @brief 单点命令转换为字节数组
 * @details 将单点命令结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化。
 * 
 * @param command 单点命令结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据序列化
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_single_command
 * @see validate_single_command
 */
protocol_error_t single_command_to_bytes(const C_SC_NA_1_t* command,
                                       u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为单点命令
 * @details 将104协议规定的字节数组格式转换为单点命令结构。
 *          按照协议规范进行数据反序列化。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param command 单点命令结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据反序列化
 * @warning 数据长度必须足够容纳单点命令
 * 
 * @see single_command_to_bytes
 * @see validate_single_command
 */
protocol_error_t bytes_to_single_command(const u8* data, u32 length,
                                       C_SC_NA_1_t* command);

/**
 * @brief 双点命令转换为字节数组
 * @details 将双点命令结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化。
 * 
 * @param command 双点命令结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据序列化
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_double_command
 * @see validate_double_command
 */
protocol_error_t double_command_to_bytes(const C_DC_NA_1_t* command,
                                       u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为双点命令
 * @details 将104协议规定的字节数组格式转换为双点命令结构。
 *          按照协议规范进行数据反序列化。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param command 双点命令结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据反序列化
 * @warning 数据长度必须足够容纳双点命令
 * 
 * @see double_command_to_bytes
 * @see validate_double_command
 */
protocol_error_t bytes_to_double_command(const u8* data, u32 length,
                                       C_DC_NA_1_t* command);

/**
 * @brief 带时标的单点命令转换为字节数组
 * @details 将带时标的单点命令结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化，包含时间戳。
 * 
 * @param command 带时标的单点命令结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据序列化，包含时间戳
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_single_command_with_time
 * @see validate_single_command_with_time
 */
protocol_error_t single_command_with_time_to_bytes(const C_SC_TA_1_t* command,
                                                 u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为带时标的单点命令
 * @details 将104协议规定的字节数组格式转换为带时标的单点命令结构。
 *          按照协议规范进行数据反序列化，包含时间戳。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param command 带时标的单点命令结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据反序列化，包含时间戳
 * @warning 数据长度必须足够容纳带时标的单点命令
 * 
 * @see single_command_with_time_to_bytes
 * @see validate_single_command_with_time
 */
protocol_error_t bytes_to_single_command_with_time(const u8* data, u32 length,
                                                 C_SC_TA_1_t* command);

/**
 * @brief 调节命令转换为字节数组
 * @details 将调节命令结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化。
 * 
 * @param command 调节命令结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据序列化
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_regulate_command
 * @see validate_regulate_command
 */
protocol_error_t regulate_command_to_bytes(const C_RC_NA_1_t* command,
                                         u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为调节命令
 * @details 将104协议规定的字节数组格式转换为调节命令结构。
 *          按照协议规范进行数据反序列化。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param command 调节命令结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_VALUE: 数据值无效
 * 
 * @note 按照104协议规范进行数据反序列化
 * @warning 数据长度必须足够容纳调节命令
 * 
 * @see regulate_command_to_bytes
 * @see validate_regulate_command
 */
protocol_error_t bytes_to_regulate_command(const u8* data, u32 length,
                                         C_RC_NA_1_t* command);

/* ==================== 遥控数据管理函数 ==================== */

/**
 * @brief 初始化遥控数据点配置表
 * @param configs 遥控数据点配置数组
 * @param max_count 最大配置个数
 * @return 协议错误码
 */
protocol_error_t init_remote_control_point_config_table(remote_control_point_config_t* configs, u32 max_count);

/**
 * @brief 添加遥控数据点配置
 * @param configs 遥控数据点配置数组
 * @param count 当前配置个数指针
 * @param max_count 最大配置个数
 * @param config 要添加的配置
 * @return 协议错误码
 */
protocol_error_t add_remote_control_point_config(remote_control_point_config_t* configs,
                                                 u32* count, u32 max_count,
                                                 const remote_control_point_config_t* config);

/**
 * @brief 更新遥控数据点配置
 * @param configs 遥控数据点配置数组
 * @param count 配置个数
 * @param point_id 点号
 * @param config 新的配置
 * @return 协议错误码
 */
protocol_error_t update_remote_control_point_config(remote_control_point_config_t* configs,
                                                   u32 count, u16 point_id,
                                                   const remote_control_point_config_t* config);

/**
 * @brief 删除遥控数据点配置
 * @param configs 遥控数据点配置数组
 * @param count 当前配置个数指针
 * @param point_id 点号
 * @return 协议错误码
 */
protocol_error_t remove_remote_control_point_config(remote_control_point_config_t* configs,
                                                   u32* count, u16 point_id);

/**
 * @brief 清空遥控数据组
 * @param group 遥控数据组结构指针
 * @return 协议错误码
 */
protocol_error_t clear_remote_control_data_group(remote_control_data_group_t* group);

/**
 * @brief 销毁遥控数据组
 * @param group 遥控数据组结构指针
 * @return 协议错误码
 */
protocol_error_t destroy_remote_control_data_group(remote_control_data_group_t* group);

/* ==================== 辅助验证函数 ==================== */

/**
 * @brief 验证单点命令品质描述词
 * @details 验证SCQ（单点命令品质描述词）是否在104协议规定的有效范围内。
 *          检查保留位是否都为0。
 * 
 * @param scq 单点命令品质描述词
 * @return 验证结果
 *         - 1: SCQ有效
 *         - 0: SCQ无效
 * 
 * @note SCQ只有1位有效，其他位必须为0
 * @warning 保留位必须为0
 * 
 * @see is_diq_valid
 * @see is_qoc_valid
 * @see scq_t
 */
int is_scq_valid(const scq_t* scq);

/**
 * @brief 验证双点命令品质描述词
 * @details 验证DCQ（双点命令品质描述词）是否在104协议规定的有效范围内。
 *          检查保留位是否都为0。
 * 
 * @param dcq 双点命令品质描述词
 * @return 验证结果
 *         - 1: DCQ有效
 *         - 0: DCQ无效
 * 
 * @note DCQ只有2位有效，其他位必须为0
 * @warning 保留位必须为0
 * 
 * @see is_qos_valid
 * @see is_qoc_valid
 * @see dcq_t
 */
int is_dcq_valid(const dcq_t* dcq);

/* ==================== 辅助函数声明 ==================== */

/**
 * @brief 获取单点命令大小
 * @details 获取单点命令结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 单点命令大小（字节）
 * 
 * @note 单点命令大小：4字节（地址3字节 + SCQ1字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_double_command_size
 * @see get_single_command_with_time_size
 * @see C_SC_NA_1_t
 */
u32 get_single_command_size(void);

/**
 * @brief 获取双点命令大小
 * @details 获取双点命令结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 双点命令大小（字节）
 * 
 * @note 双点命令大小：4字节（地址3字节 + DCQ1字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_single_command_size
 * @see get_double_command_with_time_size
 * @see C_DC_NA_1_t
 */
u32 get_double_command_size(void);

/**
 * @brief 获取带时标的单点命令大小
 * @details 获取带时标的单点命令结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 带时标的单点命令大小（字节）
 * 
 * @note 带时标的单点命令大小：11字节（地址3字节 + SCQ1字节 + 时间戳7字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_single_command_size
 * @see get_double_command_with_time_size
 * @see C_SC_TA_1_t
 */
u32 get_single_command_with_time_size(void);

/**
 * @brief 获取调节命令大小
 * @details 获取调节命令结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 调节命令大小（字节）
 * 
 * @note 调节命令大小：6字节（地址3字节 + 值2字节 + QOC1字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_single_command_size
 * @see get_double_command_size
 * @see C_RC_NA_1_t
 */
u32 get_regulate_command_size(void);

#ifdef __cplusplus
}
#endif

#endif /* CONTROL_DATA_H */
